package my.edu.model.bills.bound.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.PageVO;
import my.edu.common.exception.BillsException;
import my.edu.common.utils.PageUtils;
import my.edu.constant.BillsType;
import my.edu.constant.BoundBusinessType;
import my.edu.constant.OrderNoConstant;
import my.edu.model.bills.bound.entity.OtherInboundOrder;
import my.edu.model.bills.bound.entity.OtherOutboundOrder;
import my.edu.model.bills.bound.entity.OtherOutboundOrderDetail;
import my.edu.model.bills.bound.entity.dto.InboundDetailDTO;
import my.edu.model.bills.bound.entity.dto.OutboundDTO;
import my.edu.model.bills.bound.entity.dto.OutboundDetailDTO;
import my.edu.model.bills.bound.entity.dto.OutboundOrderSearchDTO;
import my.edu.model.bills.bound.entity.vo.OutboundDetailVO;
import my.edu.model.bills.bound.entity.vo.OutboundVO;
import my.edu.utils.ChildrenLoader;
import my.edu.utils.OrderNoUtil;
import my.edu.utils.OrderServiceUtil;
import my.edu.websocket.BillWebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import my.edu.model.bills.bound.mapper.OtherOutboundOrderMapper;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Service
public class OtherOutboundOrderServiceImpl extends ServiceImpl<OtherOutboundOrderMapper, OtherOutboundOrder> implements IOtherOutboundOrderService{

    @Autowired
    private OrderServiceUtil orderServiceUtil;
    @Autowired
    private IOtherOutboundOrderDetailService otherOutboundOrderDetailService;
    @Autowired
    private BoundBusinessType businessType;
    @Autowired
    private OrderNoConstant orderNoConstant;
    @Autowired
    private BillWebSocket billWebSocket;

    @Override
    @Transactional
    public void saveNew(OutboundDTO outboundDTO) {
        checkDTO(outboundDTO);
        OtherOutboundOrder otherOutboundOrder = BeanUtil.copyProperties(outboundDTO, OtherOutboundOrder.class, "outboundDetailDTOS");
        setField(otherOutboundOrder,outboundDTO.getOutboundDetailDTOS(),true);
        save(otherOutboundOrder);
        otherOutboundOrderDetailService.saveByDTO(outboundDTO.getOutboundDetailDTOS(), otherOutboundOrder);
        billWebSocket.sendToAllClient(BillsType.OTHEROUTBOUND,"单据类型：出库单;单据编号："+otherOutboundOrder.getOrderNo()+";提交人："+otherOutboundOrder.getCreatedName());

    }

    private void setField(OtherOutboundOrder outboundOrder,List<OutboundDetailDTO> outboundDetailDTOList,boolean isNew) {
        if (outboundOrder.getOrderNo() == null || outboundOrder.getOrderNo().isEmpty()) {
            //设置No
            String orderNo = OrderNoUtil.generateOrderNo(orderNoConstant.getOtherInboundNo());
            outboundOrder.setOrderNo(orderNo);
        } else {
            //检查是否有重复no
            long count = count(Wrappers.<OtherOutboundOrder>lambdaQuery().eq(OtherOutboundOrder::getOrderNo, outboundOrder.getOrderNo()));
            if (isNew && count > 0) throw new BillsException(MessageConstant.ORDER_NO_EXISTS);
        }
        Integer quantity = 0;
        BigDecimal amount = BigDecimal.ZERO;
        for (OutboundDetailDTO outboundDetailDTO : outboundDetailDTOList) {
            quantity+=outboundDetailDTO.getQuantity();
            amount = amount.add(outboundDetailDTO.getOutboundPrice().multiply(BigDecimal.valueOf(outboundDetailDTO.getQuantity())));
        }
        outboundOrder.setQuantity(quantity);
        outboundOrder.setAmount(amount);
        outboundOrder.setIsAudited(0);

    }

    private void checkDTO(OutboundDTO outboundDTO) {
        orderServiceUtil.checkDTO(outboundDTO.getDeliveryMethod(), null, outboundDTO.getSalespersonId(), null, outboundDTO.getCustomerId());
        List<String> boundBusinessType = businessType.getOutboundBusinessType();
        boolean contains = boundBusinessType.contains(outboundDTO.getBusinessType());
        if (!contains)throw new BillsException(MessageConstant.BOUND_BUSINESS_TYPE_NOT_FOUND);
    }

    @Override
    @Transactional
    public void updateByDTO(OutboundDTO outboundDTO) {
        OtherOutboundOrder byId = getById(outboundDTO.getId());
        if (byId==null)throw new BillsException(MessageConstant.ORDER_NOT_EXIST);
        if (byId.getIsAudited() != null && byId.getIsAudited() == 1)
            throw new BillsException(MessageConstant.ISAUDIT_CAN_NOT_UPDATE);
        checkDTO(outboundDTO);
        BeanUtil.copyProperties(outboundDTO, byId, "outboundDetailDTOS");
        setField(byId,outboundDTO.getOutboundDetailDTOS(),false);
        updateById(byId);
        otherOutboundOrderDetailService.updateByDTO(outboundDTO.getOutboundDetailDTOS(), byId);
        billWebSocket.sendToAllClient(BillsType.OTHEROUTBOUND,"更新;单据类型：出库单;单据编号："+byId.getOrderNo()+";提交人："+byId.getUpdateName());

    }

    @Override
    @Transactional
    public void removeOrder(List<Long> longs) {
        List<OtherOutboundOrder> otherOutboundOrders = listByIds(longs);
        for (OtherOutboundOrder otherOutboundOrder : otherOutboundOrders) {
            if (otherOutboundOrder.getIsAudited() != null && otherOutboundOrder.getIsAudited() == 1)
                throw new BillsException(otherOutboundOrder.getOrderNo() + MessageConstant.ISAUDIT_CAN_NOT_DEL);
        }
        removeBatchByIds(otherOutboundOrders);
        otherOutboundOrderDetailService.removeByOrderId(longs);
    }

    @Override
    public void auditOrder(List<Long> longs) {
        updateAudit(longs,true);
    }

    private void updateAudit(List<Long> longs, boolean isAudit) {
        List<OtherOutboundOrder> list = list(Wrappers.<OtherOutboundOrder>lambdaQuery().in(OtherOutboundOrder::getId, longs));
        for (OtherOutboundOrder order : list) {
            if (isAudit){
                if (order.getIsAudited() != null && order.getIsAudited() == 1)
                    throw new BillsException(order.getOrderNo() + MessageConstant.ISAUDITED);
            }else if (order.getIsAudited() != null && order.getIsAudited() == 0){
                throw new BillsException(order.getOrderNo() + MessageConstant.NOTAUDITED);
            }
        }
        if (isAudit){
            orderServiceUtil.setAuditStatus(this, longs, true);
            otherOutboundOrderDetailService.subStockByOrderId(longs);
        }else {
            orderServiceUtil.setAuditStatus(this, longs, false);
            otherOutboundOrderDetailService.addStockByOrderId(longs);
        }
    }


    @Override
    public void rauditOrder(List<Long> longs) {
        updateAudit(longs,false);

    }

    @Override
    public PageVO<OutboundVO> getOrderList(PageDTO page, OutboundOrderSearchDTO searchDTO) {
        MPJLambdaWrapper<OtherOutboundOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(OtherOutboundOrder.class, OutboundVO.class)
                .leftJoin(OtherOutboundOrderDetail.class, OtherOutboundOrderDetail::getOtherOutboundOrderId, OtherOutboundOrder::getId)
                .like(searchDTO.getOrderNo() != null && !searchDTO.getOrderNo().isEmpty(),
                        OtherOutboundOrder::getOrderNo, searchDTO.getOrderNo())
                .between(searchDTO.getBeginTime() != null,
                        OtherOutboundOrder::getOrderDate, searchDTO.getBeginTime(), searchDTO.getEndTime())
                .eq(searchDTO.getBoundBusinessType() != null && !searchDTO.getBoundBusinessType().isEmpty(),
                        OtherOutboundOrder::getBusinessType, searchDTO.getBoundBusinessType())
                .eq(searchDTO.getIsAudited() != null,
                        OtherOutboundOrder::getIsAudited, searchDTO.getIsAudited())
                .eq(searchDTO.getSalespersonId() != null,
                        OtherOutboundOrder::getSalespersonId, searchDTO.getSalespersonId())
                .eq(searchDTO.getAuditorId() != null,
                        OtherOutboundOrder::getAuditorId, searchDTO.getAuditorId())
                .eq(searchDTO.getCustomerId() != null,
                        OtherOutboundOrder::getCustomerId, searchDTO.getCustomerId())
                .eq(searchDTO.getDeliveryMethod() != null,
                        OtherOutboundOrder::getDeliveryMethod, searchDTO.getDeliveryMethod())
                .orderByDesc(OtherOutboundOrder::getId)
        ;
        Page<OutboundVO> outboundVOPage = baseMapper.selectJoinPage(new Page<>(page.getPage(), page.getSize()), OutboundVO.class, wrapper);
        if (CollUtil.isNotEmpty(outboundVOPage.getRecords())) {
            ChildrenLoader.loadAndFillDTO(
                    outboundVOPage.getRecords(),
                    OutboundVO::getId,
                    ids -> otherOutboundOrderDetailService.list(
                            Wrappers.<OtherOutboundOrderDetail>lambdaQuery().in(OtherOutboundOrderDetail::getOtherOutboundOrderId, ids)),
                    OutboundDetailVO::getOtherOutboundOrderId,
                    OutboundDetailVO.class,
                    OutboundVO::setOutboundDetailVOList);
        };
        return PageUtils.toPage(outboundVOPage);
    }

//    @Async
//    protected  void  setVoDetail(OutboundVO vo){
//        vo.setOutboundDetailVOList(
//                BeanUtil.copyToList(
//                        otherOutboundOrderDetailService.list(Wrappers.<OtherOutboundOrderDetail>lambdaQuery().eq(OtherOutboundOrderDetail::getOtherOutboundOrderId, vo.getId()))
//                        , OutboundDetailVO.class
//                )
//        );
//    }


    @Override
    public OutboundVO getOrderOne(Long id) {
        MPJLambdaWrapper<OtherOutboundOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(OtherOutboundOrder.class, OutboundVO.class)
                .selectCollection(OtherOutboundOrderDetail.class, OutboundVO::getOutboundDetailVOList)
                .leftJoin(OtherOutboundOrderDetail.class, OtherOutboundOrderDetail::getOtherOutboundOrderId, OtherOutboundOrder::getId)
                    .eq(OtherOutboundOrder::getId, id);
        return baseMapper.selectJoinOne(OutboundVO.class, wrapper);
    }
}